#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    /* --- 设置应用相关属性 --- */

    /* 应用程序名 */
    this->setWindowTitle("串口曲线工具");

    /* 窗口大小 */
    this->setFixedSize(this->width(),this->height());

    /* 应用左上角ICON */
    this->setWindowIcon(QIcon(":/new/prefix1/wave_log.png"));

    /* windows 状态栏图标 */
    QSystemTrayIcon * trayIcon = new QSystemTrayIcon();
    trayIcon->setIcon(QIcon(":/new/prefix1/wave_log.png"));
    trayIcon->show();

    /* --- 创建定时器, 周期扫描串口资源 --- */
    scanTimer = new QTimer();
    scanTimer->setInterval(1000);   /* 扫描间隔1000ms */
    connect(scanTimer, &QTimer::timeout, this, &Widget::on_timerTimeout);
    scanTimer->start();             /* 默认开启定时器 */

    /* --- 创建串口 --- */
    serialPort  = new QSerialPort();

    /* --- 绘制曲线窗口 --- */
    /* 生成游标 */
    tracer = new QCPItemTracer(ui->waveWidget);                            /* 生成游标 */
    tracer->setPen(QPen(Qt::red));                                         /* 轮廓颜色 */
    tracer->setBrush(QBrush(Qt::red));                                     /* 填充颜色 */
    tracer->setStyle(QCPItemTracer::tsCircle);                             /* 圆圈样式 */
    tracer->setSize(5);                                                    /* 大小 */

    /* 曲线1 */
    QPen pen1;
    pen1.setColor(Qt::red);
    pen1.setWidth(1);                /* 设置曲线线宽 */

    ui->waveWidget->addGraph();
    ui->waveWidget->graph(0)->setName("euler");     /* 设置数据系列的名称, 这个名称将显示在图例中 */
    ui->waveWidget->graph(0)->rescaleAxes(true);    /* 坐标轴自适应曲线 */
    ui->waveWidget->graph(0)->setPen(pen1);         /* 设置曲线颜色 */
    //ui->waveWidget->graph(0)->setLineStyle(QCPGraph::lsNone);      /* 是否连线 */
    ui->waveWidget->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 1));   /* 曲线上数据大小 */

    /* 曲线2 */
    QPen pen2;
    pen2.setColor(Qt::green);
    pen2.setWidth(1);                /* 设置曲线线宽 */

    ui->waveWidget->addGraph();
    ui->waveWidget->graph(1)->setName("acc");     /* 设置数据系列的名称, 这个名称将显示在图例中 */
    ui->waveWidget->graph(1)->rescaleAxes(true);
    ui->waveWidget->graph(1)->setPen(pen2);
    //ui->waveWidget->graph(1)->setLineStyle(QCPGraph::lsNone);      /* 是否连线 */
    ui->waveWidget->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 1));   /* 曲线上数据大小 */

    /* 曲线3 */
    QPen pen3;
    pen3.setColor(Qt::blue);
    pen3.setWidth(1);                /* 设置曲线线宽 */

    ui->waveWidget->addGraph();
    ui->waveWidget->graph(2)->setName("gyro");     /* 设置数据系列的名称, 这个名称将显示在图例中 */
    ui->waveWidget->graph(2)->rescaleAxes(true);
    ui->waveWidget->graph(2)->setPen(pen3);
    //ui->waveWidget->graph(2)->setLineStyle(QCPGraph::lsNone);      /* 是否连线 */
    ui->waveWidget->graph(2)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 1));   /* 曲线上数据大小 */

    /* 曲线坐标相关 */
    ui->waveWidget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    ui->waveWidget->legend->setVisible(true);                   /* 显示右上角图例 */
    ui->waveWidget->legend->setFont(QFont("Helvetica", 8));    /* 设置图例字体大小 */
    ui->waveWidget->xAxis->setRange(0, 800);                    /* 设置x轴坐标 */
    ui->waveWidget->yAxis->setRange(-1.0, 1.0);                 /* 设置y轴坐标 */

    connect(ui->waveWidget, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(on_MouseMoveShowTracer(QMouseEvent *)));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::init_SerialPort(QSerialPort * port, QString name, QString baud)
{
    /* 设置串口名称 */
    port->setPortName(name);

    /* 配置波特率 */
    if (baud == "115200")
    {
        port->setBaudRate(QSerialPort::Baud115200);
    }
    else if(baud == "2000000")
    {
        port->setBaudRate(2000000);
    }

    /* 配置数据位 */
    port->setDataBits(QSerialPort::Data8);

    /* 配置校验位 */
    port->setParity(QSerialPort::NoParity);

    /* 配置停止位 */
    port->setStopBits(QSerialPort::OneStop);

    /* 无流控 */
    port->setFlowControl(QSerialPort::NoFlowControl);

    return;
}

void Widget::serial_ReceiveData()
{
    QByteArray array = serialPort->readAll();
    QString    str_rev;

    /* --- 将接收到的数据显示在接收串口 --- */
    str_rev += QString(array.toHex(' ').toUpper().append(' ')); /* 转化为16进制 */

    /* 判断是否显示接收到的数据 */
    if (isShowRecvData == true)
    {
        ui->recvTextEdit->insertPlainText(str_rev);
    }

    /* 分割字符串 */
    QStringList hexList = str_rev.split(' ');

    /* 存储转换后的十六进制数据 */
    QList<quint8> byteArray;

    /* 遍历并转换 */
    for (const QString &hexStr : hexList)
    {
        bool ok;
        quint8 byte = hexStr.toInt(&ok, 16); /* 按十六进制解析 */

        if (ok)
        {
            byteArray.append(byte);
        }
        else
        {

        }
    }

    /* --- 解析出欧拉角 --- */
    float roll_ang, pitch_ang, yaw_ang;
    float x_acc, y_acc, z_acc;
    float x_gyro, y_gyro, z_gyro;

    /* 遍历字节数组（确保不越界）*/
    for (int i = 0; i <= (byteArray.size() - 4); ++i)
    {
        /* 检查当前及后续两个字节是否匹配目标 */
        if (byteArray[i] == 0x55 && byteArray[i+1] == 0x53) /* 欧拉角 */
        {
            switch (byteArray[i+2])
            {
                case 0x01:
                {
                    /* 解析 */
                    roll_ang = ((int) byteArray[i+7] << 24) |((int) byteArray[i+6] << 16) | \
                               ((int) byteArray[i+5] << 8) |((int) byteArray[i+4] << 0);
                    roll_ang /= 1000.0f;

                    /* 画图 */
                    if (isEnWaveShow == true)
                    {
                        ui->rtValLabel->setText(QString::number(roll_ang));         /* 显示实时数据 */

                        ui->waveWidget->graph(0)->addData(wavePointIdx, roll_ang);  /* 添加新数据 */

                        /* x坐标移动 */
                        if (wavePointIdx >= 800)
                        {
                            ui->waveWidget->xAxis->setRange((wavePointIdx - 800), 800, Qt::AlignLeft);
                        }

                        ui->waveWidget->replot();                              /* 重新绘图 */
                        wavePointIdx++; /* 绘图下坐标++ */
                    }
                } break;

                case 0x02:
                {
                    pitch_ang = ((int) byteArray[i+7] << 24) |((int) byteArray[i+6] << 16) | \
                               ((int) byteArray[i+5] << 8) |((int) byteArray[i+4] << 0);

                    pitch_ang /= 1000.0f;
                } break;

                case 0x03:
                {
                    yaw_ang = ((int) byteArray[i+7] << 24) |((int) byteArray[i+6] << 16) | \
                               ((int) byteArray[i+5] << 8) |((int) byteArray[i+4] << 0);

                    yaw_ang /= 1000.0f;
                } break;

                default:break;
            }
        }
        else if (byteArray[i] == 0x55 && byteArray[i+1] == 0x51) /* 加速度值 */
        {
            x_acc = (((short) (byteArray[i+3] << 8) | byteArray[i+2]) / 32768.0f) * 16 * 9.8f;
            y_acc = (((short) (byteArray[i+5] << 8) | byteArray[i+4]) / 32768.0f) * 16 * 9.8f;
            z_acc = (((short) (byteArray[i+7] << 8) | byteArray[i+6]) / 32768.0f) * 16 * 9.8f;

            /* 画图 */
            if (isEnWaveShow == true)
            {
                ui->waveWidget->graph(1)->addData(wavePointIdx, y_acc);  /* 添加新数据 */
            }
        }
        else if (byteArray[i] == 0x55 && byteArray[i+1] == 0x52) /* 角速度值 */
        {
            x_gyro = (((short) (byteArray[i+3] << 8) | byteArray[i+2]) / 32768.0f) * 2000.0f;
            y_gyro = (((short) (byteArray[i+5] << 8) | byteArray[i+4]) / 32768.0f) * 2000.0f;
            z_gyro = (((short) (byteArray[i+7] << 8) | byteArray[i+6]) / 32768.0f) * 2000.0f;

            /* 画图 */
            if (isEnWaveShow == true)
            {
                ui->waveWidget->graph(2)->addData(wavePointIdx, y_gyro);  /* 添加新数据 */
            }
        }
    }
}

/* --- 无归类槽函数 --- */

void Widget::on_timerTimeout()
{
    /* 搜索当前可用串口，并保存在QList中 */
    QList<QSerialPortInfo> list = QSerialPortInfo::availablePorts();

    ui->serialComboBox->clear();

    /* 遍历list加入下拉列表, 并打印串口信息 */
    foreach (const QSerialPortInfo &info, list)
    {
        ui->serialComboBox->addItem(info.portName());
    }

    return;
}

/* --- 串口操作部分槽函数 --- */

void Widget::on_portOCPushButton_clicked()
{
    /* 判断串口打开状态 */
    if (isOpenPort == false)
    {
        QString name, baud, errinfo, port_info;

        /* 提取选中的串口信息 */
        name = ui->serialComboBox->currentText();
        baud = ui->baudComboBox->currentText();

        /* 获取被打开串口的信息 */
        foreach (const QSerialPortInfo &portInfo, QSerialPortInfo::availablePorts())
        {
            if (portInfo.portName() == name)
            {
                port_info = portInfo.description(); /* 保存描述 */
                break;
            }
        }

        /* 初始化send串口 */
        init_SerialPort(serialPort, name, baud);

        /* 打开串口, 并判断打开结果 */
        bool state = serialPort->open(QIODevice::ReadWrite);

        if (state == true)
        {
            /* 已打开指定串口, 停止扫描串口 */
            scanTimer->stop();
        }
        else
        {
            /* 打开失败, 获取错误信息 */
            errinfo = serialPort->errorString();
        }

        /* 再次判断串口是否打开 */
        if (serialPort->isOpen())
        {
            /* 标记串口已打开 */
            isOpenPort = true;

            /* 修改按键名 */
            ui->portOCPushButton->setText("关闭串口");

            /* 设置状态提示 */
            ui->statusLabel->setText("打开成功：" + name + ' ' + '(' + port_info + ')');
            ui->statusLabel->setStyleSheet("color:green");
        }
        else
        {
            /* 标记串口已关闭 */
            isOpenPort = false;

            /* 设置状态提示 */
            ui->statusLabel->setText(errinfo);
            ui->statusLabel->setStyleSheet("color:red");
        }

        /* 已打开串口, 开始接收串口数据 */
        if (isOpenPort == true)
        {
            connect(serialPort, &QSerialPort::readyRead, this, &Widget::serial_ReceiveData);
        }
    }
    else
    {
        if (serialPort->isOpen())
        {
            /* 关闭串口, 重新开始扫描 */
            serialPort->close();
            scanTimer->start();

            /* 标记已关闭 */
            isOpenPort = false;

            /* 修改按键名 */
            ui->portOCPushButton->setText("打开串口");

            /* 设置状态提示 */
            ui->statusLabel->setText("关闭成功");
            ui->statusLabel->setStyleSheet("color:green");
        }
        else
        {
            /* 设置状态提示 */
            ui->statusLabel->setText("没有打开");
            ui->statusLabel->setStyleSheet("color:red");
        }
    }

    return;
}

/* --- 接收数据部分槽函数 --- */

void Widget::on_pauseRecvPushButton_clicked()
{
    if (isEnRecvData == true)
    {
        /* 暂停接收串口数据 */
        serialPort->blockSignals(true); /* 阻断信号产生 */

        isEnRecvData = false;
        ui->pauseRecvPushButton->setText("开始");
    }
    else
    {
        /* 开始接收串口数据 */
        serialPort->blockSignals(false); /* 恢复信号产生 */

        isEnRecvData = true;
        ui->pauseRecvPushButton->setText("暂停");
    }
}

void Widget::on_clearRecvPushButton_clicked()
{
    /* 清除接收区数据 */
    ui->recvTextEdit->clear();
}

void Widget::on_recvShowCheckBox_stateChanged(int arg1)
{
    bool state;

    (void) arg1;

    /* 判断勾选状态 */
    state = ui->recvShowCheckBox->isChecked();

    if (state)
    {
        isShowRecvData = true;
    }
    else
    {
        isShowRecvData = false;
    }
}

void Widget::on_recvHexCheckBox_stateChanged(int arg1)
{
    bool state;

    (void) arg1;

    /* 判断勾选状态 */
    state = ui->recvHexCheckBox->isChecked();

    if (state)
    {
        isHexRecvData = true;
    }
    else
    {
        isHexRecvData = false;
    }
}

/* 波形显示部分槽函数 */

void Widget::on_waveEnPushButton_clicked()
{
    if (isEnWaveShow == true)
    {
        isEnWaveShow = false;
        ui->waveEnPushButton->setText("开始");
    }
    else
    {
        isEnWaveShow = true;
        ui->waveEnPushButton->setText("暂停");
    }
}

void Widget::on_waveClearPushButton_clicked()
{
    /* 清除曲线 */
    ui->waveWidget->graph(0)->data().data()->clear();
    ui->waveWidget->graph(1)->data().data()->clear();
    ui->waveWidget->graph(2)->data().data()->clear();

    /* x坐标清0 */
    ui->waveWidget->xAxis->setRange(0, 800);        /* 设置x轴坐标 */
    wavePointIdx = 0;

    ui->waveWidget->replot();                       /* 重新绘图 */
}

void Widget::on_MouseMoveShowTracer(QMouseEvent * event)
{
    int   x = 0;
    float y = 0;

    x = ui->waveWidget->xAxis->pixelToCoord(event->pos().x());
    y = ui->waveWidget->yAxis->pixelToCoord(event->pos().y());

    ui->rtXPosLabel->setText(QString::number(x));
    ui->rtYPosLabel->setText(QString::number(y));
}
